home *** CD-ROM | disk | FTP | other *** search
-
-
- """socket interactions for gadfly client and server"""
-
- from select import select
-
- # responses
-
- SUCCESS = "SUCCESS"
- EXCEPTION = "EXCEPTION"
-
- def reply_exception(exception, info, socket):
- """send an exception back to the client"""
- # any error is invisible to client
- from gfserve import ServerError
- import sys
- try:
- reply( (EXCEPTION, (exception, info)), socket)
- except:
- #info = "%s %s" % (sys.exc_type, sys.exc_value)
- socket.close()
- #raise ServerError, "reply_exception failed: "+`info`
-
- def reply_success(data, socket):
- """report success with data back to client"""
- reply( (SUCCESS, data), socket)
-
- def reply(data, socket):
- from marshal import dumps
- marshaldata = dumps(data)
- send_packet(socket, marshaldata)
- socket.close()
-
- def send_packet(socket, data):
- """blast out a length marked packet"""
- send_len(data, socket)
- socket.send(data)
-
- def send_len(data, socket):
- """send length of data as cr terminated int rep"""
- info = `len(data)`+"\n"
- socket.send(info)
-
- def send_certified_action(actor_name, action, arguments, password, socket):
- from marshal import dumps
- marshaldata = dumps( (action, arguments) )
- cert = certificate(marshaldata, password)
- #print actor_name, cert, marshaldata
- marshaldata = dumps( (actor_name, cert, marshaldata) )
- send_packet(socket, marshaldata)
-
- def unpack_certified_data(data):
- from marshal import loads
- # sanity check
- unpack = (actor_name, certificate, marshaldata) = loads(data)
- return unpack
-
- def recv_data(socket, timeout=10):
- """receive data or time out"""
- from time import time
- endtime = time() + timeout
- reader = Packet_Reader(socket)
- done = 0
- while not done:
- timeout = endtime - time()
- if timeout<0:
- raise IOError, "socket time out (1)"
- (readable, dummy, error) = select([socket], [], [socket], timeout)
- if error:
- raise IOError, "socket in error state"
- if not readable:
- raise IOError, "socket time out (2)"
- reader.poll()
- done = (reader.mode==READY)
- return reader.data
-
- def interpret_response(data):
- """interpret response data, raise exception if needed"""
- from marshal import loads
- (indicator, data) = loads(data)
- if indicator==SUCCESS:
- return data
- elif indicator==EXCEPTION:
- # ???
- raise EXCEPTION, data
- else:
- raise ValueError, "unknown indicator: "+`indicator`
-
- # packet reader modes
- LEN = "LEN"
- DATA = "DATA"
- READY = "READY"
- ERROR = "ERROR"
-
- BLOCK_SIZE = 4028
-
- LEN_LIMIT = BLOCK_SIZE * 10
-
- class Packet_Reader:
- """nonblocking pseudo-packet reader."""
-
- # packets come in as decimal_len\ndata
- # (note: cr! not crlf)
-
- # kick too large requests if set
- limit_len = LEN_LIMIT
-
- def __init__(self, socket):
- self.socket = socket
- self.length = None
- self.length_remaining = None
- self.len_list = []
- self.data_list = []
- self.received = ""
- self.data = None
- self.mode = LEN
-
- def __len__(self):
- if self.mode is LEN:
- raise ValueError, "still reading length"
- return self.length
-
- def get_data(self):
- if self.mode is not READY:
- raise ValueError, "still reading"
- return self.data
-
- def poll(self):
- mode = self.mode
- if mode is READY:
- raise ValueError, "data is ready"
- if mode is ERROR:
- raise ValueError, "socket error previously detected"
- socket = self.socket
- (readable, dummy, error) = select([socket], [], [socket], 0)
- if error:
- self.socket.close()
- self.mode = ERROR
- raise ValueError, "socket is in error state"
- if readable:
- if mode is LEN:
- self.read_len()
- # note: do not fall thru automatically
- elif mode is DATA:
- self.read_data()
-
- def read_len(self):
- """assume socket is readable now, read length"""
- socket = self.socket
- received = self.received
- len_list = self.len_list
- if not received:
- # 10 bytes at a time until len is read.
- received = socket.recv(10)
- while received:
- # consume, test one char
- input = received[0]
- received = received[1:]
- if input == "\n":
- # done reading length
- from string import join, atoi
- try:
- length = self.length = atoi(join(len_list, ""))
- except:
- self.mode = ERROR
- socket.close()
- raise ValueError, "bad len string? "+`len_list`
- self.received = received
- self.length_remaining = length
- self.mode = DATA
- limit_len = self.limit_len
- if limit_len and length>limit_len:
- raise ValueError, "Length too big: "+`(length, limit_len)`
- return
- if len(len_list)>10:
- self.mode = ERROR
- socket.close()
- raise ValueError, "len_list too long: "+`len_list`
- len_list.append(input)
- if not received:
- (readable, dummy, error) = select(\
- [socket], [], [socket], 0)
- if error:
- self.mode = ERROR
- socket.close()
- raise ValueError, "socket in error state"
- if readable:
- received = socket.recv(10)
- # remember extra data received.
- self.received = received
-
- def read_data(self):
- # assume socket is readable
- socket = self.socket
- received = self.received
- length_remaining = self.length_remaining
- data_list = self.data_list
- if received:
- data_list.append(received)
- self.received = ""
- length_remaining = length_remaining - len(received)
- recv_len = max(length_remaining, BLOCK_SIZE)
- received = socket.recv(recv_len)
- if received:
- data_list.append(received)
- length_remaining = length_remaining - len(received)
- if length_remaining<1:
- self.mode = READY
- from string import join
- self.data = join(data_list, "")
- self.length_remaining = length_remaining
-
- def certificate(String, password):
- """generate a certificate for a string, using a password"""
- from md5 import new
- if not String:
- raise ValueError, "cannot generate certificate for empty string"
- taggedstring = password + String
- return new(taggedstring).digest()
-
- def certify(String, cert, password):
- """check a certificate for a string"""
- return certificate(String, password) == cert
-
-